﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Sl.Log;
using Sl.Support.McuBytes;
using Sl.Duplex;

namespace Cc9d{

	public class Cc9dCodec  :IDisposable {
		public Logger Log { get; set; }
		private readonly IDuplex Port;
		public Action<byte> dRecvEcmd = null;

		public Cc9dCodec(IDuplex port) {
			this.Port = port;
			port.dDataToRead = readData;
		}

		private bool isDisposed = false; // 标志对象是否已被释放
		public bool IsDisposed => isDisposed; // 提供只读属性供外部检查
		public void Dispose() {
			if (!isDisposed) {
				Port.Dispose();
				dRecvPkg = null;
				isDisposed = true;
			}
		}

		private void readData(object sender) {
			byte[] ba = new byte[64];
			while (true) {
				try {
					int recvnum;
					try {
						recvnum = Port.Read(ba, 0, 64);
						if (recvnum <= 0) {
							Log?.LogLine($"【警告】端口断开了，接收结束。（{recvnum}）");
							return;
						}
					} catch (System.TimeoutException ex) {
						return;
					} 
					for (int i = 0; i < recvnum; i++) {
						recvByte(ba[i]);
					}
				} catch (IOException e) {
					Log?.LogLine($"【警告】端口发生了异常：{e}。");
					return;
				}
			}
		}
		
		const byte Separator = 0x9d;
		const byte Escape = 0x9e;

		const byte Crc_fail = 0x91;
		const byte Too_long = 0x92;

		const byte esc_9d = 0xfe;
		const byte esc_9e = 0xff;
		const byte no_exc = 0x80;



		#region 接收程序
		public Action<Pkg> dRecvPkg;
		int esc_count = 0;
		/// <summary>
		/// 这个函数从带有转义的流中解析出一个不带转义的数据和转义后的控制数据
		/// </summary>
		/// <param name="b">收到的数据 b</param>
		void recvByte(byte b) {
			if (b == Separator) {
				recvEnd();
			} else if (b == Escape) {
				esc_count++;
			}else {
				if (esc_count != 0) {
					esc_count--;
					if (b >= 0xfe) {
						recvData((byte)(b - esc_9d + Separator));//b is fe => 9D ;b is ff => 9E
					} else {
						if (dRecvEcmd != null) {
							dRecvEcmd.Invoke(b); // 处理转义命令
						} else {
							Log?.LogLine($"【消息】收到转义命令{b:x2}，但没有注册处理函数。");
						}
					}
				} else {
					recvData(b);
				}
			}
		}
		Pkg recv_pkg;
		const int Abandon = -1;
		int recv_ptr = 0;
		void recvData(byte data) {
			if (recv_ptr == Abandon) {
				return;
			}
			if (recv_ptr == 0) {
				recv_pkg = new Pkg(); 
				recv_pkg.Is_send = false;
			}
			if (recv_ptr >= Pkg.PKG_SIZE) {//如果收数据量超过了缓冲区
				recv_ptr = Abandon;//通常这是总线错误或者插拔造成误码或者错过包头造成的
				return;
			}
			recv_pkg.Buf[recv_ptr] = data;
			recv_ptr++;
		}
		void recvEnd() {
			if (recv_ptr >= 5) {//必须收到4个地址参数加一个CRC
				finishRecv();
			}
			recv_pkg = null;
			recv_ptr = 0;
		}

		void finishRecv() {
			int recv_len = recv_ptr;
			byte crc = 0xff;//先验证CRC			
			for (int i = 0; i < recv_len; i++) {
				Crc8h31.crcInput(ref crc, recv_pkg.Buf[i]);
			}
			if (crc != 0) {//crc 错误
				Log?.LogLine($"【警告】接收发生了CRC错误包: {recv_pkg.Buf.toHexStr(0, recv_len)}-crc:{crc}");
				return;
			} else {
				recv_pkg.Len = recv_len;
				dRecvPkg?.Invoke(recv_pkg);
			}
		}

		#endregion

		#region 发送程序

		object send_Lock = new object();
		public void send(Pkg pkg) {
			lock (send_Lock) {
				Port.WriteByte(Separator);
				byte crc = 0xff;
				for (int i = 0; i < pkg.Len - 1; i++) { //len是考虑CRC的长度
					sendByte(pkg.Buf[i]);
					Crc8h31.crcInput(ref crc, pkg.Buf[i]);
				}
				sendByte(crc);
				Port.WriteByte(Separator);
			}			
			void sendByte(byte b) {
				if (b == Separator) {
					Port.WriteByte(Escape);
					Port.WriteByte(esc_9d);
				} else if (b == Escape) {
					Port.WriteByte(Escape);
					Port.WriteByte(esc_9e);
				} else {
					Port.WriteByte(b);
				}
			}
		}
		public void sendEcmd(byte esc_cmd) {
			if(esc_cmd == esc_9d || esc_cmd == esc_9e || esc_cmd == Escape || esc_cmd == Separator) {
				throw new ArgumentException($"escCmd 不能是0x{esc_cmd:2X}", nameof(esc_cmd));
			}
			lock (send_Lock) {
				Port.WriteByte(Escape); // 0x9e
				Port.WriteByte(esc_cmd); // 不是 0xfe/0xff
			}
		}
		public void sendSeparator() {
			lock (send_Lock) {
				Port.WriteByte(Separator); // 0x9e
			}
		}
		#endregion
	}
}
